package com.qezhhnjy.leetcode.question.daily;

/**
 * @author qezhhnjy
 * @date 2020/12/9-21:07
 * 一个机器人位于一个 m x n 网格的左上角 （起始点在下图中标记为 “Start” ）。
 * <p>
 * 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角（在下图中标记为 “Finish” ）。
 * <p>
 * 问总共有多少条不同的路径？
 * <p>
 * 来源：力扣（LeetCode）
 * 链接：https://leetcode-cn.com/problems/unique-paths
 */
public class UniquePath {

    public static int uniquePaths(int m, int n) {
        int paths = 0;
        if (m == 1 && n == 1) return 1;
        if (m > 1) paths += uniquePaths(m - 1, n);
        if (n > 1) paths += uniquePaths(m, n - 1);
        return paths;
    }

    public static void main(String[] args) {
        System.out.println(uniquePaths(3, 7));
        System.out.println(dynamicProgram(3, 7));
    }

    /**
     * 方法一：动态规划
     * 思路与算法
     * <p>
     * 我们用 f(i, j) 表示从左上角走到 (i, j) 的路径数量，其中 i 和 j 的范围分别是 [0, m)和 [0, n)。
     * <p>
     * 由于我们每一步只能从向下或者向右移动一步，因此要想走到 (i, j)，如果向下走一步，那么会从 (i-1, j)走过来；
     * 如果向右走一步，那么会从 (i, j-1)走过来。因此我们可以写出动态规划转移方程：
     * <p>
     * f(i, j) = f(i-1, j) + f(i, j-1)
     * <p>
     * 需要注意的是，如果 i=0，那么 f(i-1,j) 并不是一个满足要求的状态，我们需要忽略这一项；同理，如果 j=0，
     * 那么 f(i,j-1)并不是一个满足要求的状态，我们需要忽略这一项。
     * <p>
     * 初始条件为 f(0,0)=1，即从左上角走到左上角有一种方法。
     * <p>
     * 最终的答案即为 f(m-1,n-1)。
     * <p>
     * 细节
     * <p>
     * 为了方便代码编写，我们可以将所有的 f(0, j)以及 f(i, 0)都设置为边界条件，它们的值均为 1。
     * <p>
     * 复杂度分析
     * <p>
     * 时间复杂度：O(mn)。
     * <p>
     * 空间复杂度：O(mn)，即为存储所有状态需要的空间。注意到 f(i, j) 仅与第 i 行和第 i-1行的状态有关，
     * 因此我们可以使用滚动数组代替代码中的二维数组，使空间复杂度降低为 O(n)。此外，由于我们交换行列的值并不会对答案产生影响，
     * 因此我们总可以通过交换 m 和 n 使得 m≤n，这样空间复杂度降低至 O(min(m,n))。
     * <p>
     */
    public static int dynamicProgram(int m, int n) {
        int[][] matrix = new int[m][n];

        for (int i = 0; i < m; i++) {
            matrix[i][0] = 1;
        }

        for (int i = 0; i < n; i++) {
            matrix[0][i] = 1;
        }

        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                matrix[i][j] = matrix[i - 1][j] + matrix[i][j - 1];
            }
        }
        return matrix[m - 1][n - 1];
    }

    /**
     * 方法二：组合数学
     * 思路与算法
     * 从左上角到右下角的过程中，我们需要移动 m+n-2次，其中有 m-1次向下移动，n-1次向右移动。因此路径的总数，就等于从 m+n-2次移动中选择 m-1次向下移动的方案数
     */
    public static int combine(int m, int n) {
        long result = 1;
        for (int x = n, y = 1; y < m; ++x, ++y) {
            result = result * x / y;
        }
        return (int) result;
    }
}
